﻿using System;
using System.Collections.Generic;
using Glue.Internals;
using Xunit;

namespace GlueTests
{
    public class NestedObjectCreatorTest
    {
        private readonly CreatorContainer creatorContainer;
        private readonly AccessorFactory accessorFactory;
        private readonly IObjectMemberAccessor bAccessor;
        private readonly IObjectMemberAccessor cAccessor;
        private readonly A theObject;
        private readonly NestedObjectCreator nestedObjectCreator;
        private readonly IObjectMemberAccessor readOnlyCAccessor;
        private readonly IObjectMemberAccessor dAccessor;
        private IObjectMemberAccessor eAccessor;

        public class A { public B B { get; set; } }
        public class B
        {
            public C C { get; set; }
            public C readOnlyC { get; private set; }
            public String Text { get; set; }

        }
        public class C
        {
            public D D { get; set; }
            public String Text { get; set; }
        }

        public class D
        {
            public E E { get; set; }
            public String Text { get; set; }
        }

        public class E
        {
            public E(String iAmNotParameterless)
            {
                Text = iAmNotParameterless;
            }
            public String Text { get; set; }
        }

        public NestedObjectCreatorTest()
        {
            creatorContainer = new CreatorContainer();
            creatorContainer.Add(typeof(B), x => new B());
            creatorContainer.Add(typeof(C), x => new C());
            accessorFactory = new AccessorFactory();
            bAccessor = accessorFactory.Create(typeof(A).GetProperty("B"));
            cAccessor = accessorFactory.Create(typeof(B).GetProperty("C"));
            dAccessor = accessorFactory.Create(typeof(C).GetProperty("D"));
            eAccessor = accessorFactory.Create(typeof(D).GetProperty("E"));
            readOnlyCAccessor = accessorFactory.Create(typeof(B).GetProperty("readOnlyC"));
            nestedObjectCreator = new NestedObjectCreator(creatorContainer);
            theObject = new A();
        }

        [Fact]
        public void Should_create_nested_object()
        {   
            nestedObjectCreator.TryCreateAllObjectsInPath(theObject, 
                new List<IObjectMemberAccessor> {bAccessor});

            Assert.NotNull(theObject.B);
        }

        [Fact]
        public void Should_create_nested_objects()
        {
            nestedObjectCreator.TryCreateAllObjectsInPath(theObject, 
                new List<IObjectMemberAccessor> { bAccessor,cAccessor });

            Assert.NotNull(theObject.B);
            Assert.NotNull(theObject.B.C);
        }

        [Fact]
        public void Should_not_create_anything_if_any_of_the_ObjectMembers_are_readonly()
        {

            nestedObjectCreator.TryCreateAllObjectsInPath(theObject,
                new List<IObjectMemberAccessor> { bAccessor, readOnlyCAccessor });

            Assert.Null(theObject.B);
        }

        [Fact]
        public void Should_create_object_if_parameterless_constructor_are_present()
        {
            nestedObjectCreator.TryCreateAllObjectsInPath(theObject,
                new List<IObjectMemberAccessor> { bAccessor, cAccessor, dAccessor });

            Assert.NotNull(theObject.B.C.D);
        }

        [Fact]
        public void Should_signal_with_return_statement_if_any_of_the_ObjectMembers_are_readonly()
        {
             Assert.False(nestedObjectCreator.TryCreateAllObjectsInPath(theObject, new List<IObjectMemberAccessor> { bAccessor, readOnlyCAccessor }));
        }

        [Fact]
        public void Should_signal_with_return_statement_if_any_creators_are_not_present()
        {
            Assert.False(nestedObjectCreator.TryCreateAllObjectsInPath(theObject, 
                new List<IObjectMemberAccessor> { bAccessor, cAccessor, dAccessor, eAccessor }));

            Assert.Null(theObject.B);
        }
        

        [Fact]
        public void Should_not_create_object_if_the_ObjectMember_is_not_null()
        {
            var b = new B();
            theObject.B = b;
            nestedObjectCreator.TryCreateAllObjectsInPath(theObject,
                new List<IObjectMemberAccessor> { bAccessor, cAccessor });

            Assert.Same(b,theObject.B);
            Assert.NotNull(theObject.B.C);
        }
    }
}
